/*
 * Copyright (C) 2010 Szymon Dembek
 * 
 * This source code is inspired from the source code of the official Android
 * Music Widget.
 * 
 * http://android.git.kernel.org/?p=platform/packages/apps/Music.git
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.demboos.galaxy.music.widget;

import java.util.List;

import com.android.music.player.service.ICorePlayerService;

import android.app.ActivityManager;
import android.app.Service;
import android.app.ActivityManager.RunningServiceInfo;
import android.appwidget.AppWidgetManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ServiceInfo;
import android.os.Binder;
import android.os.Environment;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

/**
 * Background service that refreshes the music widget
 * 
 * @author Szymon Dembek
 */
public class GalaxyMediaPlaybackService extends Service {

	public static final String CMDAPPWIDGETUPDATE = "appwidgetupdate";
	public static final String CMDNAME = "command";

	static final String TAG = "GalaxySMusicWidget";

	public static final String SAMSUNG_INT_SERVICE_PACKAGE = "com.android.music";
	public static final String SAMSUNG_INT_PLAYER_ACTIVITY_INTENT = "com.android.music.player.activity.MusicPlayer";
	public static final String SAMSUNG_INT_PLAYLIST_ACTIVITY_INTENT = "com.android.music.list.activity.MpMainTabActivity";
	public static final String SAMSUNG_INT_SERVICE_CLASS = "com.android.music.player.service.CorePlayerService";

	public static final String SAMSUNG_CUST_SERVICE_PACKAGE = "com.sec.android.app.music";
	public static final String SAMSUNG_CUST_PLAYER_ACTIVITY_INTENT = "com.sec.android.app.music.player.activity.MusicPlayer";
	public static final String SAMSUNG_CUST_PLAYLIST_ACTIVITY_INTENT = "com.sec.android.app.music.list.activity.MpMainTabActivity";
	public static final String SAMSUNG_CUST_SERVICE_CLASS = "com.sec.android.app.music.player.service.CorePlayerService";

	public static String SAMSUNG_SERVICE_PACKAGE;
	public static String SAMSUNG_PLAYER_ACTIVITY_INTENT;
	public static String SAMSUNG_PLAYLIST_ACTIVITY_INTENT;
	public static String SAMSUNG_SERVICE_CLASS;

	// intent action names from the Samsung music player
	public static final String META_CHANGED = "musicPlayer.service.updateMediaInfo";
	public static final String PLAYBACK_COMPLETE = "musicPlayer.service.updatePlayComplete";
	public static final String PLAYSTATE_CHANGED = "musicPlayer.service.updatePlayInfo";
	public static String PREV_ACTION;
	public static String NEXT_ACTION;
	public static String TOGGLEPAUSE_ACTION;

	public static final String INT_PREV_ACTION = "com.android.music.musicservicecommand.previous";
	public static final String CUST_PREV_ACTION = "com.sec.android.app.music.musicservicecommand.previous";
	public static final String INT_NEXT_ACTION = "com.android.music.musicservicecommand.next";
	public static final String CUST_NEXT_ACTION = "com.sec.android.app.music.musicservicecommand.next";
	public static final String INT_TOGGLEPAUSE_ACTION = "com.android.music.musicservicecommand.togglepause";
	public static final String CUST_TOGGLEPAUSE_ACTION = "com.sec.android.app.music.musicservicecommand.togglepause";

	// internal commands for play control
	public static final String NEXT_ACTION_INTERNAL = "com.demboos.galaxy.music.widget.next";
	public static final String PREV_ACTION_INTERNAL = "com.demboos.galaxy.music.widget.prev";
	public static final String TOGGLEPAUSE_ACTION_INTERNAL = "com.demboos.galaxy.music.widget.togglepause";

	public static final String SERVICECMDX = "com.demboos.galaxy.music.widget.musicservicecommand";
	public static final String SERVICE_CONNECTED = "com.demboos.galaxy.music.widget.serviceconnected";

	private GalaxyMediaAppWidgetProvider mAppWidgetProvider = GalaxyMediaAppWidgetProvider
			.getInstance();
	private ICorePlayerService mBoundServiceInt = null;
	private com.sec.android.app.music.player.service.ICorePlayerService mBoundServiceCust = null;
	private ServiceConnection mConnection = new GalaxyServiceConnection();
	private BroadcastReceiver mIntentReceiver = new GalaxyServiceReceiver();
	private BroadcastReceiver mIntentCardReceiver = new SDCardStatusReceiver();
	private boolean mIsBound = false;
	private boolean mIsMediaReady = false;

	private final IBinder mBinder = new ServiceBinder();

	private void log(String msg) {
		Log.d(TAG, msg);
	}

	@Override
	public void onCreate() {
		super.onCreate();
		log("service - onCreate");

		setUpStrings();

		if (isCoreServiceRunning()) {
			log("service - core service running - doing bind");
			doBindService();
		}

		String status = Environment.getExternalStorageState();
		if (status.equals(Environment.MEDIA_SHARED)
				|| status.equals(Environment.MEDIA_UNMOUNTED)
				|| status.equals(Environment.MEDIA_REMOVED)) {
			log("service - media unavailable");
			mIsMediaReady = false;
		} else {
			log("service - media available");
			mIsMediaReady = true;
		}

		IntentFilter intentFilter = new IntentFilter();
		intentFilter.addAction(SERVICECMDX);
		intentFilter.addAction(PLAYBACK_COMPLETE);
		intentFilter.addAction(META_CHANGED);
		intentFilter.addAction(PLAYSTATE_CHANGED);
		intentFilter.addAction(PREV_ACTION_INTERNAL);
		intentFilter.addAction(NEXT_ACTION_INTERNAL);
		intentFilter.addAction(TOGGLEPAUSE_ACTION_INTERNAL);
		registerReceiver(mIntentReceiver, intentFilter);

		intentFilter = new IntentFilter();
		intentFilter.addAction(Intent.ACTION_MEDIA_BAD_REMOVAL);
		intentFilter.addAction(Intent.ACTION_MEDIA_EJECT);
		intentFilter.addAction(Intent.ACTION_MEDIA_SHARED);
		intentFilter.addAction(Intent.ACTION_MEDIA_UNMOUNTED);
		intentFilter.addAction(Intent.ACTION_MEDIA_MOUNTED);
		intentFilter.addDataScheme("file");
		registerReceiver(mIntentCardReceiver, intentFilter);
	}

	private boolean isCoreServiceRunning() {
		ActivityManager am = (ActivityManager) this
				.getSystemService(ACTIVITY_SERVICE);
		List<RunningServiceInfo> listOfServices = am.getRunningServices(100);
		for (RunningServiceInfo serviceInfo : listOfServices) {
			if (serviceInfo.service.getClassName()
					.equals(SAMSUNG_SERVICE_CLASS)) {
				return true;
			}
		}

		return false;
	}

	@Override
	public void onDestroy() {
		log("service - onDestroy");
		super.onDestroy();

		unregisterReceiver(mIntentReceiver);
		unregisterReceiver(mIntentCardReceiver);

		doUnbindService();
	}

	private void doBindService() {
		log("service - doBindService");

		if (!mIsBound || !isCoreServiceRunning()) {
			log("service - doBindService, bind neccessary");
			Intent i = new Intent();
			i.setClassName(SAMSUNG_SERVICE_PACKAGE, SAMSUNG_SERVICE_CLASS);

			mIsBound = bindService(i, mConnection, BIND_AUTO_CREATE);

			if (mIsBound) {
				log("service - onBind, bind successful");
			}
		}
	}

	private void doUnbindService() {
		log("service - doUnbindService");
		if (mIsBound) {
			log("service - doUnbindService, unBind neccessary");
			unbindService(mConnection);

			mIsBound = false;
		}
	}

	public boolean isCurrentMedia() {
		if (mIsBound) {
			if (mBoundServiceInt != null || mBoundServiceCust != null) {
				try {
					String result = null;
					if (isInternationalVersion()) {
						result = mBoundServiceInt.getCurrentMedia();
					} else if (isCustomisedVersion()) {
						result = mBoundServiceCust.getCurrentMedia();
					}
					if (result == null) {
						return false;
					} else {
						return true;
					}
				} catch (RemoteException e) {
					log("service - isCurrentMedia error:" + e.getMessage());
				} catch (Exception e) {
					log("service - isCurrentMedia error:" + e.getMessage());
				}
			}
		}

		return false;
	}

	public String getArtistName() {
		if (mIsBound) {
			if (mBoundServiceInt != null || mBoundServiceCust != null) {
				try {
					if (isInternationalVersion()) {
						return mBoundServiceInt.getCurrentAlbumInfo().mArtist;
					} else if (isCustomisedVersion())  {
						return mBoundServiceCust.getCurrentAlbumInfo().mArtist;
					}
				} catch (RemoteException e) {
					log("service - getArtistName error:" + e.getMessage());
				} catch (Exception e) {
					log("service - getArtistName error:" + e.getMessage());
				}
			}
		}

		return "";
	}

	public String getTrackName() {
		if (mIsBound) {
			if (mBoundServiceInt != null || mBoundServiceCust != null) {
				try {
					if (isInternationalVersion()) {
						return mBoundServiceInt.getCurrentAlbumInfo().mTitle;
					} else if (isCustomisedVersion()) {
						return mBoundServiceCust.getCurrentAlbumInfo().mTitle;
					}
				} catch (RemoteException e) {
					log("service - getTrackName error:" + e.getMessage());
				} catch (Exception e) {
					log("service - getTrackName error:" + e.getMessage());
				}
			}
		}

		return "";
	}

	public boolean isPlaying() {
		if (mIsBound) {
			if (mBoundServiceInt != null || mBoundServiceCust != null) {
				log("isPlaying, service not null");
				try {
					if (isInternationalVersion()) {
						return mBoundServiceInt.isPlaying();
					} else if (isCustomisedVersion()) {
						return mBoundServiceCust.isPlaying();
					}
				} catch (RemoteException e) {
					log("service - isPlaying error:" + e.getMessage());
				} catch (Exception e) {
					log("service - isPlaying error:" + e.getMessage());
				}
			}
		}

		return false;
	}

	public boolean isMediaReady() {
		return mIsMediaReady;
	}

	private void setUpStrings() {
		if (isInternationalVersion()) {
			SAMSUNG_SERVICE_PACKAGE = SAMSUNG_INT_SERVICE_PACKAGE;
			SAMSUNG_PLAYER_ACTIVITY_INTENT = SAMSUNG_INT_PLAYER_ACTIVITY_INTENT;
			SAMSUNG_PLAYLIST_ACTIVITY_INTENT = SAMSUNG_INT_PLAYLIST_ACTIVITY_INTENT;
			SAMSUNG_SERVICE_CLASS = SAMSUNG_INT_SERVICE_CLASS;

			PREV_ACTION = INT_PREV_ACTION;
			NEXT_ACTION = INT_NEXT_ACTION;
			TOGGLEPAUSE_ACTION = INT_TOGGLEPAUSE_ACTION;
		} else if (isCustomisedVersion()) {
			SAMSUNG_SERVICE_PACKAGE = SAMSUNG_CUST_SERVICE_PACKAGE;
			SAMSUNG_PLAYER_ACTIVITY_INTENT = SAMSUNG_CUST_PLAYER_ACTIVITY_INTENT;
			SAMSUNG_PLAYLIST_ACTIVITY_INTENT = SAMSUNG_CUST_PLAYLIST_ACTIVITY_INTENT;
			SAMSUNG_SERVICE_CLASS = SAMSUNG_CUST_SERVICE_CLASS;

			PREV_ACTION = CUST_PREV_ACTION;
			NEXT_ACTION = CUST_NEXT_ACTION;
			TOGGLEPAUSE_ACTION = CUST_TOGGLEPAUSE_ACTION;
		}
	}

	private boolean isInternationalVersion() {
		PackageManager pm = this.getPackageManager();

		try {
			PackageInfo pi = pm.getPackageInfo(SAMSUNG_INT_SERVICE_PACKAGE,
					PackageManager.GET_SERVICES);
			for (ServiceInfo si : pi.services) {
				if (si.name.equals(SAMSUNG_INT_SERVICE_CLASS)) {
					return true;
				}
			}

			return false;
		} catch (PackageManager.NameNotFoundException ntfe) {
			return false;
		}
	}

	private boolean isCustomisedVersion() {
		PackageManager pm = this.getPackageManager();

		try {
			PackageInfo pi = pm.getPackageInfo(SAMSUNG_CUST_SERVICE_PACKAGE,
					PackageManager.GET_SERVICES);
			for (ServiceInfo si : pi.services) {
				if (si.name.equals(SAMSUNG_CUST_SERVICE_CLASS)) {
					return true;
				}
			}

			return false;
		} catch (PackageManager.NameNotFoundException ntfe) {
			return false;
		}
	}

	public boolean isPlayerCompatible() {
		return isInternationalVersion() || isCustomisedVersion();
	}

	@Override
	public IBinder onBind(Intent intent) {
		log("service - onBind");
		return mBinder;
	}

	public class ServiceBinder extends Binder {
		public GalaxyMediaPlaybackService getUpdatingService() {
			return GalaxyMediaPlaybackService.this;
		}
	}

	public class GalaxyServiceConnection implements ServiceConnection {

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			log("service - onServiceCconnected");
			if (isInternationalVersion()) {
				GalaxyMediaPlaybackService.this.mBoundServiceInt = ICorePlayerService.Stub
						.asInterface(service);
			} else if (isCustomisedVersion()) {
				GalaxyMediaPlaybackService.this.mBoundServiceCust = com.sec.android.app.music.player.service.ICorePlayerService.Stub
				.asInterface(service);
			}
			GalaxyMediaPlaybackService.this.mAppWidgetProvider.notifyChange(
					GalaxyMediaPlaybackService.this, SERVICE_CONNECTED);
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
			log("service - onServiceDisconnected");
			GalaxyMediaPlaybackService.this.mBoundServiceInt = null;
			GalaxyMediaPlaybackService.this.mBoundServiceCust = null;
			
			if (!isCoreServiceRunning()) {
				mIsBound = false;
			}
			
			GalaxyMediaPlaybackService.this.mAppWidgetProvider.notifyChange(
					GalaxyMediaPlaybackService.this,
					GalaxyMediaPlaybackService.SERVICE_CONNECTED);
		}

	}

	public class SDCardStatusReceiver extends BroadcastReceiver {
		@Override
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();

			log("service - onReceive, action: " + action);

			if (Intent.ACTION_MEDIA_EJECT.equals(action)
					|| Intent.ACTION_MEDIA_UNMOUNTED.equals(action)
					|| Intent.ACTION_MEDIA_BAD_REMOVAL.equals(action)) {
				mIsMediaReady = false;
				log("service - onReceive, media unmounted");
				GalaxyMediaPlaybackService.this.mAppWidgetProvider
						.notifyChange(GalaxyMediaPlaybackService.this,
								SERVICE_CONNECTED);
			} else if (Intent.ACTION_MEDIA_MOUNTED.equals(action)) {
				mIsMediaReady = true;
				log("service - onReceive, media mounted");
				GalaxyMediaPlaybackService.this.mAppWidgetProvider
						.notifyChange(GalaxyMediaPlaybackService.this,
								SERVICE_CONNECTED);
			}
		}
	}

	public class GalaxyServiceReceiver extends BroadcastReceiver {

		@Override
		public void onReceive(Context context, Intent intent) {
			String action = intent.getAction();
			String cmd = intent.getStringExtra(CMDNAME);

			log("service - onReceive, action: " + action + ", cmd: " + cmd);

			if (SERVICECMDX.equals(action)) {
				if (CMDAPPWIDGETUPDATE.equals(cmd)) {
					int[] appWidgetIds = intent
							.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
					GalaxyMediaPlaybackService.this.mAppWidgetProvider
							.performUpdate(GalaxyMediaPlaybackService.this,
									appWidgetIds);
				}
			} else if (PLAYBACK_COMPLETE.equals(action)
					|| META_CHANGED.equals(action)
					|| PLAYSTATE_CHANGED.equals(action)) {
				log("service - onReceive, player update received");
				doBindService();
				GalaxyMediaPlaybackService.this.mAppWidgetProvider
						.notifyChange(GalaxyMediaPlaybackService.this, action);
			} else if (TOGGLEPAUSE_ACTION_INTERNAL.equals(action)) {
				log("service - onReceive, toggle pause received");
				final ComponentName serviceName = new ComponentName(
						SAMSUNG_SERVICE_PACKAGE, SAMSUNG_SERVICE_CLASS);

				Intent innerIntent = new Intent(
						GalaxyMediaPlaybackService.TOGGLEPAUSE_ACTION);
				innerIntent.setComponent(serviceName);

				context.startService(innerIntent);

				doBindService();
			} else if (NEXT_ACTION_INTERNAL.equals(action)) {
				log("service - onReceive, next received");
				final ComponentName serviceName = new ComponentName(
						SAMSUNG_SERVICE_PACKAGE, SAMSUNG_SERVICE_CLASS);

				Intent innerIntent = new Intent(
						GalaxyMediaPlaybackService.NEXT_ACTION);
				innerIntent.setComponent(serviceName);

				context.startService(innerIntent);

				doBindService();
			} else if (PREV_ACTION_INTERNAL.equals(action)) {
				log("service - onReceive, prev received");
				final ComponentName serviceName = new ComponentName(
						SAMSUNG_SERVICE_PACKAGE, SAMSUNG_SERVICE_CLASS);

				Intent innerIntent = new Intent(
						GalaxyMediaPlaybackService.PREV_ACTION);
				innerIntent.setComponent(serviceName);

				context.startService(innerIntent);

				doBindService();
			}
		}
	}
}
